<?php
/**
 * @package panel
 * @subpackage panel.environment.loggers
 */
/**
 * Logger for queries
 * @author Dmitry Bakaleinik (code)
 */
class panel__logger__query
	implements maf__i__singletone {

	/**
	 * @var maf__fso__file
	 */
	protected $shared;

	/**
	 * @var maf__fso__file
	 */
	protected $explain;

	protected $buffer;
	protected $sqltime = 0;

	protected $header = TRUE;

	/**
	 * @return panel__logger__query
	 */
	static public function getInstance() {

		static $instance;

		if( !isset($instance) ) {
			$instance = new self; }

		return $instance;

	}	# Function

	protected function __clone() {
	}	# Function

	protected function __sleep() {
	}	# Function

	protected function __wakeup() {
	}	# Function

	protected function __construct() {

		$this->shared     = new maf__fso__file(PANEL__PATH__LOGS . "/query.log");
		$this->explain    = new maf__fso__file(PANEL__PATH__LOGS . "/explain.html");

		$this->buffer = array();

	}	# Function

	public function __destruct() {

		if( !count($this->buffer) ) {
			return; }

		$message = implode("\n", $this->buffer);

		// NOTE: FOR RARE CASE WHEN shared LIKE NORMAL, BUT HAVE PROBLEMS.
		// IF CAUSED, YOU CANT CATCH THIS EXCEPTION. SEE PHP OBJECT DESTRUCT ON EOL SCRIPT.
		try {

			$this->log($message);

		} catch ( exception $e ) { panel__application::catchException($e); }

		if( isset($_SESSION) ) {
			$_SESSION["QUERIES"][] = array($this->sqltime, count($this->buffer), $this->buffer); }

	}	# Function

	public function logMail() {

		if( !count($this->buffer) ) {
			return; }

		$message = implode("\n", $this->buffer);

		panel__logger__mailer::getInstance()->log("DATABASE UPDATES", $message);

		$this->buffer = array();

	}	# Function

	protected function log($_) {

		$_ = trim((string) $_);

		$this->shared->appendContents("$_\n\n");

	}	# Function

	public function logCommand($_) {

		switch($_) {

		case "BEGIN TRANSACTION":

			$this->header = FALSE;

			$this->buffer[] = sprintf(
				"\n\n/** STARTED TRANSACTION AT %s **/\n\n%s",
				date("Y-m-d H:i:s"),
				$_
			);

			break;

		case "COMMIT":

			$this->header = TRUE;

			$this->buffer[] = sprintf(
				"%s\n\n/** COMMITTED TRANSACTION AT %s **/\n\n",
				$_,
				date("Y-m-d H:i:s")
			);

			break;

		case "ROLLBACK":

			$this->header = TRUE;

			$this->buffer[] = sprintf(
				"\n\n/** ROLLED BACK AT %s **/\n\n%s",
				date("Y-m-d H:i:s"),
				$_
			);

			break;

		default:
			$this->buffer[] = $_;
			break;

		}	# switch

	}	# Function

	public function logStatement( array $_statement_ ) {

		$queries = "";
		static $_last_use;
		static $_last_names;

		foreach( $_statement_["queries"] as $_query ) {

			if( isset($_query["use"]) && ($_last_use != $_query["use"]) ) {

				$_last_use = $_query["use"];
				$queries  .= $_query["use"] . "\n";

			}	# if

			if( isset($_query["names"]) && ($_last_names != $_query["names"]) ) {

				$_last_names = $_query["names"];

				$queries    .= sprintf(
					"SET NAMES '%s' %s;\n",
					$_query["names"][0],
					(isset($_query["names"][1]) ? (" COLLATE '" . $_query["names"][1] . "'") : "")
				);

			}	# if

			if( $_query["affected_rows"] || $_query["last_insert_id"] ) {

				$queries .=
					"/* AR: " . sprintf("%03s", $_query["affected_rows"]) .
					($_query["last_insert_id"] ? ", L: " . sprintf("%05s", $_query["last_insert_id"]) : "" ) .
					" */ ";

			}	# if

			$queries .=
				isset_or($_query["last_insert_id"]) ?
					str_ireplace("/* LastInsertID */", sprintf(" id = '%s', ", $_query["last_insert_id"]), trim($_query["sql"])) . "\n" :
					trim($_query["sql"]);

			$queries .= "\n";

		}	# foreach

		$message = "";

		if( $this->header ) {

			$message = sprintf(
				"/* %s - LID: %s - AR: %s - P: %s */\n",
				$_statement_["time"],
				$_statement_["last_insert_id"],
				$_statement_["affected_rows"],
				substr($_statement_["mtime"], 0, 5)
			);

		}	# if

		$message .= $queries;

		$this->sqltime += $_statement_["mtime"];
		$this->buffer[] = trim($message);

	}	# Function

	public function logExplain($_query_, $_explain_, $_time_, $_count_) {

		$joinsize = 1;

		foreach ($_explain_ as $_row) {

			$joinsize *= ((int) $_row["rows"] ? $_row["rows"] : 1 );

			if( panel__settings__environment::isVerbose("panel.db.explain_queries.trigger_diff_join_size") && $joinsize < $_count_ ) {
				return; }

		}	# foreach

		if( !$this->explain->isExists() || !$this->explain->getSize() ) {

			$tpl_body = <<<TPL
<html>
<body>
<style>
table, td { border: 1px solid gray; empty-cells: show; }
td { padding: 2px; font-family: Consolas; font-size: 10pt; }
pre { font-family: Consolas; font-size: 10pt; }
</style>
TPL;

			$this->explain->setContents($tpl_body);

		}	# if

		$tpl_explain = "";

		foreach ($_explain_ as $_row) {

			foreach ($_row  as $_key => &$_value) {
				$_value = htmlentities($_value); }

			$tpl_explain .= <<<TPL
<tr>
	<td>{$_row["id"]}</td>
	<td>{$_row["select_type"]}</td>
	<td>{$_row["table"]}</td>
	<td>{$_row["type"]}</td>
	<td>{$_row["possible_keys"]}</td>
	<td>{$_row["key"]}</td>
	<td>{$_row["key_len"]}</td>
	<td>{$_row["ref"]}</td>
	<td>{$_row["rows"]}</td>
	<td>{$_row["Extra"]}</td>
</tr>
TPL;



		}	# foreach


		$tpl_query = <<<TPL
<table cellspacing=0 cellpadding=0 style='width: 100%;'>
<tr>
	<td>Query: {$_time_} / Return rows count: {$_count_} / Join Size: {$joinsize}</td>
</tr>
<tr>
	<td style='overflow: scroll; max-height: 250px; width: 100%; vertical-align: top; white-space: pre; display: block;'>{$_query_}</td>
</tr>
<tr>
	<td style='overflow: scroll; max-height: 500px; vertical-align: top; display: block;'>
		<table cellspacing=0 cellpadding=0>
		<tr>
			<td>ID</td>
			<td>Select type</td>
			<td>Table</td>
			<td>Type</td>
			<td>Possible keys</td>
			<td>Key</td>
			<td>KL</td>
			<td>Ref</td>
			<td>Rows</td>
			<td>Extra</td>
		</tr>
		{$tpl_explain}
		</table>
	</td>
</tr>
</table>
<br><br>
TPL;

		$this->explain->appendContents($tpl_query);

	}	# Function

}	# Class

/* EOF */